#
# $Id: descriptor_type.rb 4 2007-09-27 09:38:28Z dinko.srkoc $
#

require 'rexml/document'
include REXML

require 'yaml'
File.open(File.join(File.dirname(__FILE__), 'dbms_data.yaml')) { |yf| $dbms_data = YAML::load(yf) }

# Currently supported Database Management Systems
SUPPORTED_DBMSs  = [ :postgresql, :informix, :mysql, :hsqldb, :sqlserver, :oracle ].freeze

# Currently supported types of descriptors
DESCRIPTOR_TYPES = [ :jpa, :hibernate_xml, :hibernate_properties ].freeze

# Currently supported JPA implementors
PERSISTENCE_IMPLEMENTATIONS = [ :hibernate_specific, :openjpa_specific, :toplink_specific ]

# Contains submodules that are specific for the requested descriptor type.
module DescriptorType

  # Returns module implementing chosen descriptor type. Raises exception
  # if supported types don't include the <tt>descriptor_type</tt> argument.
  #
  # For the list of currently supported descriptor types see DESCRIPTOR_TYPES.
  def DescriptorType.descriptor_module(descriptor_type)
    case descriptor_type
      when :jpa then DescriptorType::Jpa
      when :hibernate_properties then DescriptorType::HibernateProperties
      when :hibernate_xml then DescriptorType::HibernateXml
      else
        raise "Unknow descriptor type: #@descriptor_type"
    end
  end
  
  module Jpa
    
    # JPA <em>persistence.xml</em> template
    TEMPLATE = <<-DOC_TEMPLATE
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
       version="1.0">
       <persistence-unit>
       </persistence-unit>
    </persistence>
    DOC_TEMPLATE
    
    # Module related to Hibernate as a persistence implementor
    module HibernateImplementation
      def implementation_name; 'hibernate_implementation'; end
    end
    
    # Module related to OpenJPA as a persistence implementor
    module OpenJPAImplementation
      def implementation_name; 'openjpa_implementation'; end
    end
    
    # Module related to Toplink as a persistence implementor
    module ToplinkImplementation
      def implementation_name; 'toplink_implementation'; end
    end
    
    # Returns tuple of:
    # 1) implementation specific data (hibernate, ...)
    # 2) DBMS specific JDBC data
    def load_data
      [ $dbms_data['jpa'][implementation_name],
        @dbms ? $dbms_data['jdbc'][@dbms.type.to_s] : nil ]
    end
    
    # Adds +property+ subnodes to node +properties+
    def add_properties(impl, jdbc, &blk)
      if @dbms
        blk.call(impl['sql_dialect']['name'], impl['sql_dialect'][@dbms.type.to_s])
        blk.call(impl['driver'], jdbc['driver'])
        blk.call(impl['url'], jdbc['url'] % [ @dbms.host, @dbms.port, @dbms.database, @dbms.server ])
        blk.call(impl['username'], @dbms.username)
        blk.call(impl['password'], @dbms.password)
      end
      blk.call(impl['sql_logging']['name'], impl['sql_logging']['value']) if @options.sql_logging
      if @options.ddl_management && impl['ddl_generation'][ @options.ddl_management.to_s ] # ako postoji
        blk.call(impl['ddl_generation']['name'], impl['ddl_generation'][@options.ddl_management.to_s])
      end
    end
    
    # Main method for building the JPA descriptor
    def make_descriptor
      include_appropriate_implementation
      implementor, jdbc = load_data
      
      doc = Document.new(TEMPLATE)
      pu = doc.root.elements['persistence-unit']
      pu.add_attribute('name', @entity_manager_name)
      if @application_runs
        transaction_type = @application_runs == :locally ? 'RESOURCE_LOCAL' : 'JTA' # ???
        pu.add_attribute('transaction-type', transaction_type)
      end
      
      if @data_source
        ds = Element.new(@data_source.is_jta ? 'jta-data-source' : 'non-jta-data-source', pu)
        ds.add_text(@data_source.jndi)
      end
      
      Element.new('provider', pu).add_text(implementor['provider_class'])
      
      props = Element.new('properties', pu)
      add_properties(implementor, jdbc) { |name, value| add_property_element(props, name, value) }
      @properties.each { |p| add_property_element(props, p[:name], p[:value]) } if @properties
      
      # za kraj, snimanje gotovog opisnika na disk
      doc.write(File.new('persistence.xml', 'w'), 0)
    end
    
    # Includes appropriate persistence implementor's related module.
    # Depends on a DSL's <tt>persistence_implementation_is</tt> setting.
    def include_appropriate_implementation
      case @persistence_impl_type
        when :hibernate_specific then extend HibernateImplementation
        when :openjpa_specific   then extend OpenJPAImplementation
        when :toplink_specific   then extend ToplinkImplementation
        else
          raise "unknown persistence implementation type: #@persistence_impl_type"
      end
    end

    def add_property_element(properties, name, value)
      properties.add_element('property', 'name' => name, 'value' => value)
    end
  end # Module Jpa
  
  module HibernateXml
    
    def make_descriptor
    end
    
  end
  
  module HibernateProperties
    
    def make_descriptor
    end
    
  end
  
end